package gins

import (
	"context"
	"errors"
	"net"
	"net/http"
	"strconv"
	"time"

	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	"github.com/mars/pkg/app"
	"github.com/mars/pkg/loggers"
)

func InitGenericAPIServer(s *GenericAPIServer) {
	s.InstallMiddlewares()
	s.InstallAPIs()
}

// GenericAPIServer
type GenericAPIServer struct {
	Port            int
	Mode            string // gin 启动模式
	Healthz         bool   // 是否开启健康检查
	EnableMetrics   bool   // 是否接入 prometheus
	EnableProfiling bool   // 是否开启 golang Profile

	*gin.Engine

	httpServer *http.Server
}

func (s *GenericAPIServer) InstallAPIs() {
	// 注册健康检查接口
	if s.Healthz {
		s.GET("/f5monweb/f5check.html", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "This is a page for monitoring.")
		})
	}

	if s.EnableMetrics {
		// now(2022-08) do nothing
	}

	if s.EnableProfiling {
		pprof.Register(s.Engine)
	}

	s.GET("/version", func(ctx *gin.Context) {
		ctx.JSON(http.StatusOK, app.Get())
	})
}

// InstallMiddlewares install generic middlewares.
func (s *GenericAPIServer) InstallMiddlewares() {
	s.Use(gin.Recovery())
}

// Setup do some setup work for gin engine.
func (s *GenericAPIServer) Setup() {
	gin.SetMode(s.Mode)
	gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
		loggers.Infof("%-6s %-s --> %s (%d handlers)", httpMethod, absolutePath, handlerName, nuHandlers)
	}
}

func (s *GenericAPIServer) Run() error {
	s.httpServer = &http.Server{
		Addr:    net.JoinHostPort("", strconv.Itoa(s.Port)),
		Handler: s,
	}
	loggers.Infof("gin server start on %d PORT.", s.Port)
	if err := s.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
		loggers.Error(err.Error())
		return err
	}

	loggers.Infof("Server on %d stopped", s.Port)
	return nil
}

func (s *GenericAPIServer) Shutdown() {
	// The context is used to inform the server it has 10 seconds to finish
	// the request it is currently handling
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if err := s.httpServer.Shutdown(ctx); err != nil {
		loggers.Warnf("httpServer shutdown failed: %s", err)
	}
}
